package org.javaexpert.spaces.space;

import net.jini.core.entry.Entry;
import net.jini.core.entry.UnusableEntryException;
import net.jini.core.event.EventRegistration;
import net.jini.core.event.RemoteEventListener;
import net.jini.core.lease.Lease;
import net.jini.core.lease.LeaseDeniedException;
import net.jini.core.lease.LeaseMap;
import net.jini.core.lease.UnknownLeaseException;
import net.jini.core.transaction.Transaction;
import net.jini.core.transaction.TransactionException;
import net.jini.space.JavaSpace;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.lang.reflect.Field;
import java.rmi.MarshalledObject;
import java.rmi.RemoteException;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;

public class SimpleJavaSpace implements JavaSpace {
    Random rand = new Random(System.currentTimeMillis());
    List<Entry> entries = new LinkedList<Entry>();

    public synchronized Lease write(Entry entry, Transaction transaction, long l) throws TransactionException, RemoteException {
        entries.add(rand.nextInt(entries.size() + 1), entry);
        SimpleLease lease = new SimpleLease();
        notifyAll();
        return lease;
    }

    public synchronized Entry read(Entry template, Transaction transaction, long timeout) throws UnusableEntryException, TransactionException, InterruptedException, RemoteException {
        return findMatch(template, timeout);
    }

    private Entry findMatch(Entry template, long timeout) throws InterruptedException {
        Entry res = findMatch(template);
        Long waitUntil = System.currentTimeMillis() + timeout;
        while (res == null && System.currentTimeMillis() < waitUntil) {
            wait(waitUntil - System.currentTimeMillis());
            res = findMatch(template);
        }
        return res;
    }

    private Entry findMatch(Entry template) {
        for (Entry e : entries) {
            if (matchesTemplate(e, template)) {
                return e;
            }
        }
        return null;
    }

    public static boolean matchesTemplate(Entry e, Entry template) {
        assert e != null;
        if (template == null) {
            return true;
        }
        if (!template.getClass().isAssignableFrom(e.getClass())) {
            return false;
        }
        try {
            for (Field f : template.getClass().getFields()) {
                f.isAccessible();
                Object templateFieldContent = f.get(template);
                if (templateFieldContent != null) {
                    if (!templateFieldContent.equals(f.get(e))) {
                        return false;
                    }
                }
            }
        } catch (IllegalAccessException e1) {
            throw new RuntimeException(e1);
        }
        return true;
    }

    public synchronized Entry readIfExists(Entry entry, Transaction transaction, long l) throws UnusableEntryException, TransactionException, InterruptedException, RemoteException {
        return findMatch(entry);
    }

    public synchronized Entry take(Entry template, Transaction transaction, long timeout) throws UnusableEntryException, TransactionException, InterruptedException, RemoteException {
        Entry res = findMatch(template, timeout);
        entries.remove(res);
        return res;
    }

    public synchronized Entry takeIfExists(Entry template, Transaction transaction, long l) throws UnusableEntryException, TransactionException, InterruptedException, RemoteException {
        Entry res = findMatch(template);
        entries.remove(res);
        return res;
    }

    public synchronized EventRegistration notify(Entry entry, Transaction transaction, RemoteEventListener remoteEventListener, long l, MarshalledObject marshalledObject) throws TransactionException, RemoteException {
        throw new NotImplementedException();
    }

    public synchronized Entry snapshot(Entry entry) throws RemoteException {
        return entry;
    }

    private static class SimpleLease implements Lease {
        public long getExpiration() {
            throw new NotImplementedException();
        }

        public void cancel() throws UnknownLeaseException, RemoteException {
            throw new NotImplementedException();
        }

        public void renew(long l) throws LeaseDeniedException, UnknownLeaseException, RemoteException {
            throw new NotImplementedException();
        }

        public void setSerialFormat(int i) {
            throw new NotImplementedException();
        }

        public int getSerialFormat() {
            throw new NotImplementedException();
        }

        public LeaseMap createLeaseMap(long l) {
            throw new NotImplementedException();
        }

        public boolean canBatch(Lease lease) {
            throw new NotImplementedException();
        }
    }
}
